package server

import (
	"github.com/akirakazu/go-simplejson"
)

// Proxy pack
func (s *Server) proxyPack(id string, pack *Pack) {
	if !pack.Msg.Has("to") {
		msg := json.New()
		msg.Set("err", "to missing")
		s.response(id, pack, msg)
		return
	}

	targetId := pack.Msg.Get("to")

	identity := json.New()

	if id == "" {
		if pack.Msg.Has("from") {
			identity = pack.Msg.Get("from")
			id, _ = s.findConnWithoutSelfId(identity)
		}
	} else {
		if !s.isProxyConn(id) {
			conn := s.conns[id]
			identity = conn.identity
			pack.Msg.Set("from", identity)
		} else {
			if pack.Msg.Has("from") {
				identity = pack.Msg.Get("from")
			}
		}
	}

	to, found := s.findConn(targetId, identity)
	if found {
		pack.Msg.Remove("__excludes")
		pack.Msg.Remove("__vias")
		s.send(to, pack)
		return
	}

	excludes := []string{}
	vias := []string{}
	excs, err := pack.Msg.GetStringArray("__excludes")
	if err == nil {
		excludes = excs
	}

	vs, err := pack.Msg.GetStringArray("__vias")
	if err == nil {
		vias = vs
	}

	// TODO something went wrong here
	// This is just a quick fix
	if len(excludes) > 10 || len(vias) > 10 {
		return
	}

	// To not found in local clients and caches, search through proxies
	proxy_id := ""
	clientType, err1 := targetId.GetString("client_type")
	houseId, err2 := targetId.GetString("house_id")

	if err1 == nil && clientType == "ccu" && err2 == nil {
		proxy_id = s.findProxy(excludes, vias, houseId)
	} else {
		proxy_id = s.findProxy(excludes, vias, "")
	}

	if proxy_id != "" {
		if pack.Cmd == "binary" {
			targetIdString := JSONToString(targetId)
			s.caches[targetIdString] = proxy_id
		}
		if proxy_id == PROXY_ID {
			if len(vias) != 0 && vias[len(vias)-1] == s.proxy.id {
				// do nothing
			} else {
				vias = append(vias, s.proxy.id)
				pack.Msg.Set("__vias", vias)
			}
			s.proxy.sendPack(pack)
		} else {
			session_id, err := s.conns[proxy_id].identity.GetString("session_id")
			if err == nil {
				if len(vias) != 0 && vias[len(vias)-1] == session_id {
					// do nothing
				} else {
					vias = append(vias, session_id)
					pack.Msg.Set("__vias", vias)
				}
			}
			s.send(proxy_id, pack)
		}
		return
	} else {
		if len(vias) == 0 {
			if pack.Cmd == "binary" {
				return
			} else {
				if pack.Cmd == "response" {
					error_string, err := pack.Msg.GetString("err")
					if err == nil && error_string == "no handler found" {
						return
					}
				}
				msg := json.New()
				msg.Set("err", "no handler found")
				msg.Set("to", pack.Msg.Get("from"))
				_id, _ := s.findConnWithoutSelfId(identity)
				s.response(_id, pack, msg)
			}
		} else {
			session_id := ""
			if id == "" {
				session_id = s.proxy.id
			} else {
				session_id, _ = s.conns[id].identity.GetString("session_id")
			}

			excludes = append(excludes, session_id)
			pack.Msg.Set("__excludes", excludes)

			via := vias[len(vias)-1]
			vias = vias[0 : len(vias)-1]
			pack.Msg.Set("__vias", vias)

			if s.proxy != nil && s.proxy.ready && via == s.proxy.id {
				s.proxy.sendPack(pack)
			} else {
				proxyIdentity := json.New()
				proxyIdentity.Set("session_id", via)
				proxyId, proxyFound := s.findConnWithoutSelfId(proxyIdentity)
				if proxyFound {
					s.send(proxyId, pack)
				}
			}
		}
	}
}
